New IO-APIC ACK method seems to cause problems on some systems
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 14 Apr 2006 13:14:24 +0000 (14:14 +0100)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 14 Apr 2006 13:14:24 +0000 (14:14 +0100)
(e.g., Dell 1850). Disable it by default for now, but allow the
new mwethod to be tested by passing boot parameter 'new_ack'
to Xen.

You can tell which ACK method you are using because Xen prints
out "Using old ACK method" or "Using new ACK method" during boot.

This workaround can be removed if/when the problems with the new
ACK method are flushed out.

Signed-off-by: Keir Fraser <keir@xensource.com>
xen/arch/x86/io_apic.c

index 560aaa66123396f0e0bef34cf5ac1a83468909ef..73786f5800df6a53f4a232d524aab0a50a61e195 100644 (file)
@@ -190,6 +190,18 @@ static void __unmask_IO_APIC_irq (unsigned int irq)
     __modify_IO_APIC_irq(irq, 0, 0x00010000);
 }
 
+/* trigger = 0 */
+static void __edge_IO_APIC_irq (unsigned int irq)
+{
+    __modify_IO_APIC_irq(irq, 0, 0x00008000);
+}
+
+/* trigger = 1 */
+static void __level_IO_APIC_irq (unsigned int irq)
+{
+    __modify_IO_APIC_irq(irq, 0x00008000, 0);
+}
+
 /* mask = 1, trigger = 0 */
 static void __mask_and_edge_IO_APIC_irq (unsigned int irq)
 {
@@ -1321,8 +1333,50 @@ static unsigned int startup_level_ioapic_irq (unsigned int irq)
     return 0; /* don't check for pending */
 }
 
+static int new_ack;
+boolean_param("new_ack", new_ack);
+
 static void mask_and_ack_level_ioapic_irq (unsigned int irq)
 {
+    unsigned long v;
+    int i;
+
+    if ( new_ack )
+        return;
+
+    mask_IO_APIC_irq(irq);
+/*
+ * It appears there is an erratum which affects at least version 0x11
+ * of I/O APIC (that's the 82093AA and cores integrated into various
+ * chipsets).  Under certain conditions a level-triggered interrupt is
+ * erroneously delivered as edge-triggered one but the respective IRR
+ * bit gets set nevertheless.  As a result the I/O unit expects an EOI
+ * message but it will never arrive and further interrupts are blocked
+ * from the source.  The exact reason is so far unknown, but the
+ * phenomenon was observed when two consecutive interrupt requests
+ * from a given source get delivered to the same CPU and the source is
+ * temporarily disabled in between.
+ *
+ * A workaround is to simulate an EOI message manually.  We achieve it
+ * by setting the trigger mode to edge and then to level when the edge
+ * trigger mode gets detected in the TMR of a local APIC for a
+ * level-triggered interrupt.  We mask the source for the time of the
+ * operation to prevent an edge-triggered interrupt escaping meanwhile.
+ * The idea is from Manfred Spraul.  --macro
+ */
+    i = IO_APIC_VECTOR(irq);
+
+    v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));
+
+    ack_APIC_irq();
+
+    if (!(v & (1 << (i & 0x1f)))) {
+        atomic_inc(&irq_mis_count);
+        spin_lock(&ioapic_lock);
+        __edge_IO_APIC_irq(irq);
+        __level_IO_APIC_irq(irq);
+        spin_unlock(&ioapic_lock);
+    }
 }
 
 static void end_level_ioapic_irq (unsigned int irq)
@@ -1330,6 +1384,12 @@ static void end_level_ioapic_irq (unsigned int irq)
     unsigned long v;
     int i;
 
+    if ( !new_ack )
+    {
+        unmask_IO_APIC_irq(irq);
+        return;
+    }
+
 /*
  * It appears there is an erratum which affects at least version 0x11
  * of I/O APIC (that's the 82093AA and cores integrated into various
@@ -1693,6 +1753,7 @@ void __init setup_IO_APIC(void)
         io_apic_irqs = ~PIC_IRQS;
 
     printk("ENABLING IO-APIC IRQs\n");
+    printk(" -> Using %s ACK method\n", new_ack ? "new" : "old");
 
     /*
      * Set up IO-APIC IRQ routing.